-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
fix: add symbol to components to properly validate them #12452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: add symbol to components to properly validate them #12452
Conversation
🦋 Changeset detectedLatest commit: 71fbcbd The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Maybe we should get rid of the try-catch entirely, and instead do more introspection on the passed value instead, e.g. "is this falsy or a function". |
I think that's directionally correct, yeah — the try-catch is messy and brittle. It's not quite as simple as just doing this though... export function validate_dynamic_component(component_fn) {
if (component_fn !== undefined && typeof component_fn !== 'function') {
e.svelte_component_invalid_this_value();
}
const instance = component_fn();
if (instance !== undefined && typeof instance !== 'object') {
e.svelte_component_invalid_this_value();
}
return instance;
} ...because |
Could it be worth adding a component symbol too? |
That wouldn't help here, because |
But |
The root of the issue is that |
Ohhh yeah now I see that. I was thinking....could we move this validation to validate_component? This function will always get the thunk with |
That makes sense, yeah. It would also cover this missing validation that way. We'd need to vary the error message based on whether it's a dynamic component or not |
Ok I will take a shot at it later on this branch |
…te-dynamic-component
I think I got it mostly working. Initially I used the newly introduced ORIGINAL and FILENAME to check if it was a component. Then I refactored with a new symbol because it seemed better. However it's the playground itself is kinda failing because the root layout of sveltekit doesn't have the symbol I added (I just added it after the FILENAME add and in the hmr accept block). I'm exploring why this is, I wonder if it's because those are being created with Svelte4Class instead. |
I'm an idiot...it was a typo lol |
validate_dynamic_component
@Rich-Harris this should be ready...everything works but i hope everything is correct enough 😄 Open to feedback obviously 🙂 |
I'm fine with the |
I agree, I actually thought about that. Even tho is technically possible with a proxy but it's pretty convoluted. |
I don't love this constraint, to be honest. I'm wondering if we can do away with the bootleg typechecking altogether and rely on actual typechecking. If we aligned the signature of server and client snippets so that snippets received thunks on the server, just like on the client — a tiny bit of overhead but it would allow people to 'decorate' snippets (we need a better name for it btw) just like they can decorate components — then we could get rid of the symbol stuff. Basically: export interface Component<...> {
(
this: void,
internal: ComponentInternals,
props: Props
): {
// ...
} & Exports;
element?: typeof HTMLElement;
z_$$bindings?: Bindings;
}
export interface Snippet<Parameters extends unknown[] = []> {
(
this: void,
internal: SnippetInternals,
...args: number extends Parameters['length'] ? never : Getters<Parameters>
): typeof SnippetReturn;
} This feels to me like a better solution than expanding the API surface area for a niche use case, and it pre-empts the question 'I can see the code that Svelte generates in the JS Output tab of the playground — why can't I add props and curry snippet arguments?' |
Does this mean only TS user will get those kind of warnings? So if i've interpreted this correctly you don't like the symbol idea for |
Yes, though that doesn't mean function add(a, b) {
if (typeof a !== 'numner' || isNaN(a)) {
throw new Error('first argument to `add` must be a number');
}
if (typeof b !== 'numner' || isNaN(a)) {
throw new Error('second argument to `add` must be a number');
}
return a + b;
} ...we write them like this: function add(a: number, b: number) {
return a + b;
} There's no particular reason that components and snippets should be treated differently. So yeah, I think the solution warrants a separate PR — will try and rustle one up later if no-one beats me to it |
Ok...keeping this open in case we want to revisit later. This would require also a sister PR in |
Yes (btw you don't need to quote the entire comment you're replying to! It makes these threads longer and harder to read) |
Closing in favor of #12507 |
Svelte 5 rewrite
I took a shot at closing #12446
i'm not really convinced about this fix but i also think there's no real way of avoid throwing
svelte_component_invalid_this_value
unexpectedly than being precise. Currently if the component fails it should always fail invalidate_component
and i've usedvalidate_component.name
to be a bit more safe against renaming.It also just occured that maybe we want a test to actually validate that the error throws when expected if it's not already there to avoid regressions.scratch that there's already oneFeel free to close this if it's not needed it was a 5 minutes job just because it was quicker to made than asking in the issue.
Please note that the Svelte codebase is currently being rewritten for Svelte 5. Changes should target Svelte 5, which lives on the default branch (
main
).If your PR concerns Svelte 4 (including updates to svelte.dev.docs), please ensure the base branch is
svelte-4
and notmain
.Before submitting the PR, please make sure you do the following
feat:
,fix:
,chore:
, ordocs:
.Tests and linting
pnpm test
and lint the project withpnpm lint